home *** CD-ROM | disk | FTP | other *** search
- /*----------------------------------------------------------------------------------------
-
- Lurkers
-
- Greg Anderson
- January 1994
-
- AppleLink: G.ANDERSON
- Internet: greggor@apple.com
-
- About Lurkers:
-
- Lurkers searches a directory for files that are modifiable, including files
- not in a project, files checked out for modification, and modify-read-only
- files.
-
- Search modes (pick any one):
-
- -modifiable (default) Search for files not in a project, files
- checked out for modification, and modify-read-only files
- -notmodifiable Search for files checked into a project that are not
- modifiable (neither MRO nor checked out for modification)
- -MRO Search for files that are checked into a project, but have
- been modified-read-only.
- -insomeproject Search for files with a 'ckid' resource
- -notinanyproject Search for files without a 'ckid' resource
-
- Other flags:
-
- -r Search specified directory recursively
- -s Short output (only list names of files that match
- the search criterion)
-
- Revision History:
-
- 1.0, 19 Jan 94 Initial release
- 1.1, 20 Jan 94 Lurkers being too strict in its test for checked out
- files, so it missed some. Also, I failed to check
- for errors when opening the resource fork of a file
- (oops!), so files without a resource fork reported
- the information applicable to the next item in the
- resource chain (not a problem unless you check your
- MPW shell into a project--but our team does!)
- 1.2, 25 Jan 94 Lurkers still missing files; make its test even less
- strict (pretty sure I got it right this time)
-
- ----------------------------------------------------------------------------------------*/
-
- #include <Types.h>
- #include <ctype.h>
- #include <fcntl.h>
- #include <string.h>
- #include <stdio.h>
- #include <StdLib.h>
- #include <ErrMgr.h>
- #include <CursorCtl.h>
- #include <Errors.h>
- #include <QuickDraw.h>
- #include <SysEqu.h>
- #include <Files.h>
- #include <Memory.h>
- #include <Resources.h>
- #include <CursorCtl.h>
-
- static char* usage = "# Usage - %s -modifiable|-notmodifiable|-MRO|-insomeproject|-notinanyproject [-s] [-r] directory\n";
- static long optionsSpecified = false;
-
- //
- // We know a little bit about the contents of a ckid resource:
- //
- struct ckidheader
- {
- long fUnknownID; // Different after every projector operation
- long fUnkownConstant; // Always the same
-
- short fUnknown1; // Usually 0004
- short fIsCheckedOut; // Usually 2, 3 or 4 if checked out, always 0 if not
-
- short fIsMRO; // 0001 if MRO, 0000 if not
- short fUnknown2; // Usually 0000
-
- long fUnknown3; // I don't know or care what these are
- long fUnknown4;
- long fUnknown5;
- long fUnknown6;
- long fUnknown7;
- long fUnknown8;
-
- unsigned char fProjectNameLength; // Length of project name
- unsigned char fProjectName; // null-terminated
-
- // After the project name is the name of the
- // person who checked out this file on this
- // machine (no relation to the person who currently
- // has the file checked out). Also null-terminated
-
- // After the user name is the revision number
- // of the file, in ascii and null-terminated.
-
- };
-
- //
- // Projector states that we know about:
- //
- #define kNotModifiable 1
- #define kModifiedReadOnly 2
- #define kCheckedOut 4
- #define kNotInAProject 8
-
- //
- // Global options, because there is no need to pass it around
- //
- long gOptions = 0;
- long gWantOutputForState = kModifiedReadOnly | kCheckedOut | kNotInAProject;
-
- #define kShortOutput 1
- #define kRecursive 2
-
- //
- // Some global variables that should be local, but I don't
- // want my stack to get too large, and I don't feel like
- // being clever.
- //
- // The code is very carefully written to avoid using these
- // variables after they have been reused in a recursive call.
- //
- CInfoPBRec pb;
- Str255 gFilename;
- Str255 gFolderpath;
-
- //----------------------------------------------------------------------------------------
- // BuildFolderPathname:
- //----------------------------------------------------------------------------------------
- void BuildFolderPathname(short vRefNum, long dirID, Str255 folderpath)
- {
- Str63 thisName;
- OSErr err = noErr;
-
- thisName[0] = 0;
-
- pb.dirInfo.ioCompletion = nil;
- pb.dirInfo.ioNamePtr = thisName;
- pb.dirInfo.ioResult = noErr;
- pb.dirInfo.ioVRefNum = vRefNum;
- pb.dirInfo.ioDrDirID = dirID;
- pb.dirInfo.ioFDirIndex = -1;
-
- err = PBGetCatInfo(&pb,false);
-
- if(err == noErr)
- {
- BuildFolderPathname(vRefNum, pb.dirInfo.ioDrParID, folderpath);
-
- //
- // Append 'thisName' onto folderpath
- //
- thisName[thisName[0] + 1] = 0;
- strcpy(folderpath + folderpath[0] + 1, thisName + 1);
- folderpath[0] += thisName[0];
-
- //
- // Append a colon onto folderpath
- //
- folderpath[folderpath[0] + 1] = ':';
- ++folderpath[0];
- }
- else
- {
- folderpath[0] = 0;
- }
- } // BuildFolderPathname
-
- //----------------------------------------------------------------------------------------
- // ExamineProjectorInformation:
- //----------------------------------------------------------------------------------------
- long ExamineProjectorInformation(short vRefNum, long dirID, const Str255 fileName, Str255 projectName)
- {
- Handle ckidHandle = nil;
- short resRefNum;
- long projectorInfo = kNotInAProject;
-
- resRefNum = HOpenResFile(vRefNum, dirID, fileName, fsRdPerm);
-
- if(resRefNum != -1)
- {
- ckidHandle = Get1Resource('ckid', 128);
- if(ckidHandle != nil)
- {
- struct ckidheader* ckpeek = nil;
-
- HLock(ckidHandle);
- ckpeek = *( (struct ckidheader**)ckidHandle);
-
- //
- // Look at a bit of the ckid resource
- //
- if(ckpeek->fIsCheckedOut != 0)
- projectorInfo = kCheckedOut;
- else if((ckpeek->fIsMRO & 1) != 0)
- projectorInfo = kModifiedReadOnly;
- else
- projectorInfo = kNotModifiable;
-
- projectName[0] = ckpeek->fProjectNameLength;
- strcpy(projectName + 1, &ckpeek->fProjectName);
-
- HUnlock(ckidHandle);
- }
-
- CloseResFile(resRefNum);
- }
-
- return projectorInfo;
- } // ExamineProjectorInformation
-
- //----------------------------------------------------------------------------------------
- // ProcessFile:
- //----------------------------------------------------------------------------------------
- void ProcessFile(short vRefNum, long dirID, Str255 folderpath, Str255 filename, Boolean* didOutput)
- {
- Str255 projectName;
- long fileProjectStatus = ExamineProjectorInformation(vRefNum, dirID, filename, projectName);
-
- //
- // Ignore the file unless we want output for its state
- //
- if((gWantOutputForState & fileProjectStatus) != 0)
- {
- //
- // If doing short output, only print the name of the file
- //
- if((gOptions & kShortOutput) != 0)
- {
- fprintf(stdout, "%P\n", filename);
- }
- else
- {
- //
- // At this point we are commiting to printing something; if we
- // have not printed anything yet, then add an extra blank line
- // so things look better
- //
- if(*didOutput == false)
- {
- fprintf(stdout, "\n");
- }
-
- switch(fileProjectStatus)
- {
- case kNotInAProject:
- fprintf(stdout, "File \"%P%P\" # is not in a project\n", folderpath, filename);
- break;
-
- case kNotModifiable:
- fprintf(stdout, "File \"%P%P\" # is an unmodifiable copy of a file in the project %P\n", folderpath, filename, projectName);
- break;
-
- case kModifiedReadOnly:
- fprintf(stdout, "File \"%P%P\" # is a modify-read-only copy of a file in the project %P\n", folderpath, filename, projectName);
- break;
-
- case kCheckedOut:
- fprintf(stdout, "File \"%P%P\" # is a modifiable copy of a file in the project %P\n", folderpath, filename, projectName);
- break;
- }
-
- //
- // didOutput actually means "printed a long line"; short lines don't
- // count. (This flag is used to determine if a trailing blank line
- // should be added to the output)
- //
- *didOutput = true;
- }
- }
- } // ProcessFile
-
- //----------------------------------------------------------------------------------------
- // Lurkers:
- //----------------------------------------------------------------------------------------
- void Lurkers(short vRefNum, long dirID)
- {
- OSErr err = noErr;
- short index = 1;
- Boolean didOutput = false;
-
- BuildFolderPathname(vRefNum, dirID, gFolderpath);
-
- //
- // If doing long output, tell the user what we're doing
- //
- if((gOptions & kShortOutput) == 0)
- {
- fprintf(stdout, "### Scanning folder \"%P\"\n", gFolderpath);
- fflush(stdout);
- }
-
- //
- // Walk every file in this directory
- //
- while(err == noErr)
- {
- SpinCursor(1);
-
- gFilename[0] = 0;
-
- pb.hFileInfo.ioCompletion = nil;
- pb.hFileInfo.ioNamePtr = gFilename;
- pb.hFileInfo.ioResult = noErr;
- pb.hFileInfo.ioVRefNum = vRefNum;
- pb.hFileInfo.ioDirID = dirID;
- pb.hFileInfo.ioFDirIndex = index;
-
- err = PBGetCatInfo(&pb,false);
-
- //
- // We only care about files right now...
- //
- if((err == noErr) && ((pb.hFileInfo.ioFlAttrib & (1 << 4)) == 0))
- {
- ProcessFile(vRefNum, dirID, gFolderpath, gFilename, &didOutput);
- }
-
- ++index;
- }
-
- //
- // Add another blank line if there was any output,
- // then flush stdout so that the text is actually
- // printed
- //
- if(didOutput)
- {
- fprintf(stdout, "\n");
- }
- fflush(stdout);
-
- //
- // Do we want to do a deep search?
- //
- if((gOptions & kRecursive) != 0)
- {
- index = 1;
- err = noErr;
-
- //
- // Walk every folder in this directory
- //
- while(err == noErr)
- {
- gFilename[0] = 0;
-
- pb.dirInfo.ioCompletion = nil;
- pb.dirInfo.ioNamePtr = gFilename;
- pb.dirInfo.ioResult = noErr;
- pb.dirInfo.ioVRefNum = vRefNum;
- pb.dirInfo.ioDrDirID = dirID;
- pb.dirInfo.ioFDirIndex = index;
-
- err = PBGetCatInfo(&pb,false);
-
- //
- // Call Lurkers again on every folder we find...
- //
- if((err == noErr) && ((pb.hFileInfo.ioFlAttrib & (1 << 4)) != 0))
- {
- Lurkers(vRefNum, pb.dirInfo.ioDrDirID);
- }
-
- ++index;
- }
- }
- } // Lurkers
-
-
- //----------------------------------------------------------------------------------------
- // main:
- //----------------------------------------------------------------------------------------
- main( int argc, char* argv[] )
- {
- Boolean optionsSpecified = false;
- Boolean didLurkers = false;
- short vRefNum = 0;
- long dirID = 0;
- short parms;
- short status = 0;
- OSErr err = noErr;
-
- InitCursorCtl(nil);
-
- //
- // Run through the parameters once looking for things that start with "-"
- //
- for( parms = 1; parms < argc; parms++ )
- {
- short length = strlen(argv[parms]);
-
- //
- // Look at all of the parameters that have a dash
- //
- if( argv[parms][0] == '-')
- {
- //
- // Process the specific flags
- //
- if( strcmp( argv[parms], "-modifiable" ) == 0 )
- {
- optionsSpecified = true;
- gWantOutputForState = kModifiedReadOnly | kCheckedOut | kNotInAProject;
- }
- else if( strcmp( argv[parms], "-notmodifiable" ) == 0 )
- {
- optionsSpecified = true;
- gWantOutputForState = kNotModifiable;
- }
- else if( strcmp( argv[parms], "-MRO" ) == 0 )
- {
- optionsSpecified = true;
- gWantOutputForState = kModifiedReadOnly;
- }
- else if( strcmp( argv[parms], "-notinanyproject" ) == 0 )
- {
- optionsSpecified = true;
- gWantOutputForState = kNotInAProject;
- }
- else if( strcmp( argv[parms], "-insomeproject" ) == 0 )
- {
- optionsSpecified = true;
- gWantOutputForState = kModifiedReadOnly | kCheckedOut | kNotModifiable;
- }
- else if( strcmp( argv[parms], "-r" ) == 0 )
- {
- optionsSpecified = true;
- gOptions |= kRecursive;
- }
- else if( strcmp( argv[parms], "-s" ) == 0 )
- {
- optionsSpecified = true;
- gOptions |= kShortOutput;
- }
- else
- {
- fprintf(stderr,"### %s - \"%s\" is not an option.\n", argv[0], argv[parms]);
- status = 1;
-
- break;
- }
- }
- }
-
- //
- // If all of the "-" parameters were processed okay, then run through
- // the parameters again looking for directory names
- //
- if(status == 0)
- {
- fprintf(stdout, "\n");
- fflush(stdout);
-
- for( parms = 1; parms < argc; parms++ )
- {
- //
- // If there's no dash, then this must be the name of the directory to
- // search through
- //
- if( argv[parms][0] != '-')
- {
- short length = strlen(argv[parms]);
- FSSpec tempFSSpec;
-
- strcpy(gFilename+1, argv[parms]);
- gFilename[0] = length;
-
- //
- // Make an FSSpec so that we can get the vRefNum of the specified
- // directory.
- //
- err = FSMakeFSSpec(0, 0, gFilename, &tempFSSpec);
- if(err == noErr)
- {
- vRefNum = tempFSSpec.vRefNum;
-
- //
- // Use PBGetCatInfo to get the dirID of the specified folder
- //
- pb.dirInfo.ioCompletion = nil;
- pb.dirInfo.ioNamePtr = &tempFSSpec.name;
- pb.dirInfo.ioResult = noErr;
- pb.dirInfo.ioVRefNum = vRefNum;
- pb.dirInfo.ioDrDirID = tempFSSpec.parID;
- pb.dirInfo.ioFDirIndex = 0;
-
- err = PBGetCatInfo(&pb,false);
- }
-
- //
- // Run Lurkers as soon as we get a directory
- // (this is an easy and sleazy way to support multiple
- // folders on the command line)
- //
- if(err == noErr)
- {
- dirID = pb.dirInfo.ioDrDirID;
- Lurkers(vRefNum, dirID);
- didLurkers = true;
- }
- else
- {
- fprintf(stderr, "### Error %d accessing folder %P\n", err, gFilename);
- }
- }
- }
- }
-
- //
- // if there were errors in the parameters, print usage
- //
- if( (status == 1) || (didLurkers == false) )
- {
- fprintf(stderr, usage, argv[0]);
- }
-
- return status;
- } // main
-